#if defined(linux)
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include <pwd.h>
#include <grp.h>
#include <sys/resource.h>
#endif 

//#include "scrambled_symbols.h"
#include "configfile.hxx"
#include "priv/configfileimpl.hxx"
#include <stdio.h>  // to use 'sscanf'.

#include <assert.h>
#include <iostream>

using namespace std;

// Function provided by the Configuration File Parser (config_parser.y).
extern bool Config_Start_Read(const char *file, ConfigFileImpl *cfi);

// The following global variables allow to control the behavior
// of the parser and lexers. These are builtin functionality
// generated by bison and flex.
extern int config_flex_debug;
extern int configdebug;


// Configuration file of the attacker server.
static ConfigFile _configfile;

// Variables containing all value that can be changed through
// configuration file.
string bridgelockfile;
string bridgelogfile;
string attackerlockfile;
string attackerlogfile;
string bridgehost;
tSInt32 auport;
tSInt32 atkport;
tSInt32 outerauport;
tSInt32 outeratkport;
string dbhost;
tSInt32 dbport;
string dblogin;
string dbpwd;
string dbname;
tSInt32 _debug;
string username;
string groupname;

//attackernet configuration
//tSInt32 buffersize, we will use it later maybe not;
//tSInt32 attackernetbufsize;

//auconnmanager configuration
tSInt32 threadpoolsize;
tSInt32 threadpooltopsize;
tSInt32 reqquebusypoint;


///////////////////////////////////////////////////////////////////////////////
/// \brief Read config file and set variables accordingly.
///
/// Returns 'true' if successful or 'false' if config file hasn't been found
/// or if one of the mandatory variable couldn't be found into the configuration
/// file (error in then logged into the log file).
////////////
bool ReadConfigFile()
{
  int errcount;
  const char **str_param;
  const char *str_param_list[]=
    {
      "BridgeHost",
#ifndef ATTACKERN
      //"LockFileA",
      //"LogFileA",
#else
      //MAKE_STRING(LOCKFILEA(ATTACKERN)),
      //MAKE_STRING(LOGFILEA(ATTACKERN)),
#endif
	  "BridgeLockFile",
	  "BridgeLogFile",
	  "AttackerLockFile",
	  "AttackerLogFile",
      "DBHost",
      "DBLogin",
      "DBPassword",
      "DBName",
#if defined(linux)
      "User",
      "Group",
#endif
      0
    };

  string **str_param_strg;
  string *str_param_strg_list[]=
    {
      &bridgehost,
      &bridgelockfile,
      &bridgelogfile,
      &attackerlockfile,
      &attackerlogfile,
      &dbhost,
      &dblogin,
      &dbpwd,
      &dbname,
#if defined(linux)
      &username,
      &groupname,
#endif
      0
    };

  const char **int_param;
  const char *int_param_list[]=
    {
      "AutoerPort",
      "AttackerPort",
	  "OuterAutoerPort",
	  "OuterAttackerPort",
      "DBPort",
      "ThreadPoolSize",
      "ThreadPoolTopSize",
      "ReqQueBusyPoint",
      "Debug",
      0
    };

  tSInt32 **int_param_strg;
  tSInt32 *int_param_strg_list[]=
    {
      &auport,
      &atkport,
	  &outerauport,
	  &outeratkport,
      &dbport,
      &threadpoolsize,
      &threadpooltopsize,
      &reqquebusypoint,
      &_debug,
      0
    };

  errcount=0;

  // We accept 3 (for Linux) or 2 (for Windows) different locations for the configuration file.
  string path1/*=string("/etc/"); path1.append(string(MAKE_STRING(CONFIG_FILE(BRIDGETYPE))))*/;
  string path2/*=string("etc/"); path2.append(string(MAKE_STRING(CONFIG_FILE(BRIDGETYPE))))*/;
  string path3/*=string("./"); path3.append(string(MAKE_STRING(CONFIG_FILE(BRIDGETYPE))))*/;

  // Reading configuration file 
#if defined(linux)
  if (!_configfile.Read(path3) && !_configfile.Read(path2) && !_configfile.Read(path1))
#elif defined(WIN32)
  if (!_configfile.Read(path3) && !_configfile.Read(path2))
#else
#error "Unsupported OS"
  if (false)
#endif
    {
      cerr<<"[ERROR] Failed to read config file.\n";
      exit(1);
    }

  // Assign all string variables one by one. If one is missing, then it is reported
  // and will lead to error.
  for (str_param=&str_param_list[0],
	 str_param_strg=&str_param_strg_list[0]; 
       (*str_param!=0) && (*str_param_strg!=0); 
       str_param++,str_param_strg++)
    {
      if (!_configfile.GetValue(*str_param,**str_param_strg))
	{
	  cerr<<"[ERROR] Missing '"<<*str_param<<"' parameter in config file.\n";
	  errcount++;
	}      
    }
  
  // Assign all integer variables one by one. If one is missing, then it is reported
  // and will lead to error.
  for (int_param=&int_param_list[0],
	 int_param_strg=&int_param_strg_list[0]; 
       (*int_param!=0) && (*int_param_strg!=0); 
       int_param++,int_param_strg++)
    {
      if (!_configfile.GetValue(*int_param,*int_param_strg))
	  {
	    cerr<<"[ERROR] Missing '"<<*int_param<<"' parameter in config file.\n";
	    errcount++;
	  }      
    }

  // Successful only if no error encountered.
  return errcount==0;
}



///////////////////////////////////////////////////////////////////////////////
//
////////////////////
ConfigFileImpl::ConfigFileImpl()
  : _filename(), _values(), _debug_mode(false)
{
	_values.insert(make_pair(string("User"),string("bridge")));
	_values.insert(make_pair(string("Group"),string("bridge")));
	_values.insert(make_pair(string("BridgeLockFile"),string("bridge.pid")));
	_values.insert(make_pair(string("BridgeLogFile"),string("bridge.log")));
	_values.insert(make_pair(string("AttackerLockFile"),string("attacker.pid")));
	_values.insert(make_pair(string("AttackerLogFile"),string("attacker.log")));
	
	_values.insert(make_pair(string("DBLogin"),string("fanchuan0077a")));
	_values.insert(make_pair(string("DBPassword"),string("fanchuan0077")));
	_values.insert(make_pair(string("DBName"),string("fanchuan0077a")));
	_values.insert(make_pair(string("DBPort"),string("3306")));

	_values.insert(make_pair(string("AutoerPort"),string("4018")));
	_values.insert(make_pair(string("AttackerPort"),string("4019")));

#if defined(DEBUG) || defined(_DEBUG)
	//_values.insert(make_pair(string("DBHost"),string("localhost")));
	_values.insert(make_pair(string("DBHost"),string("ftp010.cdn.hi.cn")));
	_values.insert(make_pair(string("BridgeHost"),string("118.123.19.201")));

	_values.insert(make_pair(string("OuterAutoerPort"),string("40018")));
	_values.insert(make_pair(string("OuterAttackerPort"),string("40019")));

	_values.insert(make_pair(string("ThreadPoolSize"),string("1")));
	_values.insert(make_pair(string("ThreadPoolTopSize"),string("2")));
#else
	_values.insert(make_pair(string("DBHost"),string("ftp010.cdn.hi.cn")));
	_values.insert(make_pair(string("BridgeHost"),string("118.123.19.201")));

	_values.insert(make_pair(string("OuterAutoerPort"),string("40018")));
	_values.insert(make_pair(string("OuterAttackerPort"),string("40019")));

	_values.insert(make_pair(string("ThreadPoolSize"),string("4")));
	_values.insert(make_pair(string("ThreadPoolTopSize"),string("10")));

#endif

	_values.insert(make_pair(string("Debug"),string("1")));
}

ConfigFileImpl::~ConfigFileImpl()
{
  _values.clear();
}


void ConfigFileImpl::SetDebugMode(bool mode)
{
  _debug_mode=mode;
}


bool ConfigFileImpl::Read(const string &config_file)
{
  _filename=config_file;

  config_flex_debug=_debug_mode;
  configdebug=_debug_mode;

//  return (Config_Start_Read(_filename.c_str(), this));

  return true;
}

bool ConfigFileImpl::GetValue(const string &key, string &value) const
{
  bool res;

  tContainerCIt cit;

  cit=_values.find(key);

  if (cit==_values.end())
    {
      value='\0';
      res=false;
    }
  else
    {
      value=(*cit).second;
      res=true;
    }

  return res;
}

bool ConfigFileImpl::GetValue(const string &key, tSInt32 *value) const
{
  string svalue;
  int res;

  GetValue(key,svalue);

  res=sscanf(svalue.c_str(),"%d",value);

  return res!=0;
}

void ConfigFileImpl::SetValue(const string &key, const string &value)
{
  tContainer::iterator it;

  it = _values.find(key);

  if (it== _values.end())
    {
      _values.insert(make_pair(key,value));
    }
  else
    {
      _values[key]=value;
    }
}

const string &ConfigFileImpl::Name() const 
{ 
  return _filename; 
}

///////////////////////////////////////////////////////////////////////////////
//
////////////////////
ConfigFile::ConfigFile()
{
  _impl=new ConfigFileImpl();
}

ConfigFile::ConfigFile(const ConfigFile &model)
{
#if !defined(RELEASE)
  cerr<<"ConfigFile::ConfigFile(const ConfigFile &model): This function is not supposed to be called! Crashing."<<endl; cerr.flush();
  // This constructor should never be used!
  assert(0);
#endif
}

ConfigFile::~ConfigFile()
{
  delete _impl;
}

void ConfigFile::SetDebugMode(bool mode)
{
  _impl->SetDebugMode(mode);
}

tBool ConfigFile::Read(const string &config_file)
{
  return _impl->Read(config_file);
}
 
tBool ConfigFile::GetValue(const string &key, string &value) const
{
  return _impl->GetValue(key,value);
}

tBool ConfigFile::GetValue(const string &key, tSInt32 *value) const
{
  return _impl->GetValue(key,value);
}

void ConfigFile::SetValue(const string &key, const string &value)
{
  _impl->SetValue(key,value);
}

const string &ConfigFile::Name() const
{
  return _impl->Name();
}
